package com.skyline.common.graphicsmagick;

import java.util.List;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.im4java.core.GMOperation;
import org.im4java.core.GraphicsMagickCmd;
import org.im4java.core.IMOperation;
import org.im4java.core.IdentifyCmd;
import org.im4java.process.ArrayListOutputConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.skyline.common.utils.Assert;

/**
 * 使用GraphicsMagick作为图片处理工具实现的图片操作类
 * 
 * @author wuqh
 */
public class GMImageOperations {
	private static final Logger LOGGER = LoggerFactory.getLogger(GMImageOperations.class);
	private String installPath; // GraphicsMagick的安装目录
	private String[] validExt; // 能使用GraphicsMagick的压缩的格式

	public void setInstallPath(String installPath) {
		this.installPath = installPath;
	}

	public void setValidExts(String validExts) {
		this.validExt = StringUtils.split(validExts, ",");
	}

	private void checkGMConfig() {
		Assert.notNull(installPath, "GraphicsMagick的安装目录（参数installPath）必须配置");
		Assert.notNull(validExt, "能使用GraphicsMagick的压缩的格式（参数validExts）必须配置");
	}

	/**
	 * 获取图片信息，包括图片格式，图片宽度，图片高度
	 * 
	 * @param imagePath
	 *            图片路径
	 * 
	 * @return ImageInfo,包括[图片格式，图片宽度，图片高度]
	 * */
	public ImageInfo showImageInfo(String imagePath) {
		checkGMConfig();

		ArrayListOutputConsumer output = new ArrayListOutputConsumer();

		IdentifyCmd identifyCmd = createIdentifyCmd(output);

		IMOperation op = new IMOperation();
		op.addImage();

		try {
			identifyCmd.run(op, imagePath);
		} catch (Exception e) {
			LOGGER.warn("读取文件[" + imagePath + "]失败", e);
			return null;
		}

		ImageInfo ret = getImageInfo(output);

		if (ret != null) {
			return ret;
		}

		throw new NotImageException("文件[" + imagePath + "]非有效图片格式");
	}

	private ImageInfo getImageInfo(ArrayListOutputConsumer output) {
		ImageInfo ret = null;
		List<String> cmdOutput = output.getOutput();
		String imageInfo = cmdOutput.get(0);
		int len = validExt.length;
		for (int i = 0; i < len; i++) {
			ret = parseImageInfo(imageInfo, validExt[i]);
			if (ret != null) {
				break;
			}
		}
		return ret;
	}

	private IdentifyCmd createIdentifyCmd(ArrayListOutputConsumer output) {
		IdentifyCmd identifyCmd = new IdentifyCmd(true);
		identifyCmd.setSearchPath(installPath);
		identifyCmd.setOutputConsumer(output);
		return identifyCmd;
	}

	/**
	 * 图片压缩处理，如果原图有多帧则只压缩第一帧（根据原图扩展名自动识别）
	 * 
	 * @param srcURL
	 *            源图片
	 * @param destURL
	 *            目标图片的路径
	 * @param baseSize
	 *            压缩后的最大长/宽
	 * @see #compressImage(String, String, int)
	 * @return 图片处理结果
	 * 
	 */
	public ResultState resizeImage(String srcURL, String destURL, int baseSize) {
		if (hasMultiFrame(srcURL)) {
			srcURL = srcURL + "[0]";
		}
		return scaleImage(srcURL, destURL, baseSize);
	}

	/**
	 * 剪切图片，从srcImage中以(x,y)为起点切出一张长、宽都为cropSize的图片，并缩放到finalSize大小
	 * 
	 * @param srcURL
	 *            源图片的路径
	 * @param destURL
	 *            目标图片的路径
	 * @param x
	 *            x坐标
	 * @param y
	 *            y坐标
	 * @param cropSize
	 *            需要剪切的大小
	 * @param finalSize
	 *            最终图片的大小
	 * @return 图片处理结果
	 */
	public ResultState cropAndResizeImage(String srcURL, String destURL, int x, int y, int cropSize, int finalSize) {
		GraphicsMagickCmd gmCmd = createConvertCommand();

		GMOperation op = new GMOperation();
		op.addImage();
		op.crop(cropSize, cropSize, x, y);
		op.scale(finalSize);
		op.addImage();

		return runCommand(gmCmd, op, srcURL, destURL);
	}

	/**
	 * 剪切图片，从srcImage中以(x,y)为起点切出一张长、宽都为size的图片
	 * 
	 * @param srcURL
	 *            源图片的路径
	 * @param destURL
	 *            目标图片的路径
	 * @param x
	 *            x坐标
	 * @param y
	 *            有坐标
	 * @param size
	 *            需要剪切的大小
	 * 
	 * @return 图片处理结果
	 */
	public ResultState cropImage(String srcURL, String destURL, int x, int y, int size) {
		GraphicsMagickCmd gmCmd = createConvertCommand();

		GMOperation op = new GMOperation();
		op.addImage();
		op.crop(size, size, x, y);
		op.addImage();

		return runCommand(gmCmd, op, srcURL, destURL);
	}

	/**
	 * 默认图片压缩处理方式，压缩后的图片格式由目标文件后缀控制，不加人工干预；调用者在生成目标图片路径需要带上正确的后缀方可，不特殊处理多帧和透明。
	 * 
	 * @param srcURL
	 *            源图片
	 * @param destURL
	 *            目标图片的路径
	 * @param baseSize
	 *            压缩后的最大长/宽
	 * @return 图片处理结果
	 * 
	 */
	private ResultState scaleImage(String srcURL, String destURL, int baseSize) {
		GraphicsMagickCmd gmCmd = createConvertCommand();

		GMOperation op = new GMOperation();
		op.addImage();
		op.addRawArgs("-scale", baseSize + "x" + baseSize + ">");
		op.addImage();

		return runCommand(gmCmd, op, srcURL, destURL);
	}

	private GraphicsMagickCmd createConvertCommand() {
		checkGMConfig();
		GraphicsMagickCmd gmCmd = new GraphicsMagickCmd("convert");
		gmCmd.setSearchPath(installPath);
		return gmCmd;
	}

	private ResultState runCommand(GraphicsMagickCmd gmCmd, GMOperation op, String srcURL, String destURL) {
		try {
			gmCmd.run(op, srcURL, destURL);
			return ResultState.SUCCESS;
		} catch (Exception e) {
			LOGGER.warn("执行GM命令发生异常", e);
			return ResultState.PROCESS_IMAGE_FAILED;
		}
	}

	/***
	 * 通过路径对应图片的扩展名判断是否为多帧
	 * 
	 * @param srcURL
	 *            图片路径（必须带有扩展名）
	 * */
	public boolean hasMultiFrame(String srcURL) {
		String ext = FilenameUtils.getExtension(srcURL);
		return isExtMultiFrame(ext);
	}

	/***
	 * 判断扩展名是否为多帧
	 * 
	 * @param ext
	 *            扩展名
	 * */
	public boolean isExtMultiFrame(String ext) {
		return "GIF".equalsIgnoreCase(ext);
	}

	/**
	 * GraphicsMagick中使用，identify 命令获取图片信息后解析，看图片是否为制定格式，如果是返回图片信息 否则返回空 <br/>
	 * 图片信息格式如下：文件名 格式 宽度x高度+......，其中文件名可能为文件名[0]（如果为多帧） 以此获取图片信息
	 * 
	 * @param info
	 *            GraphicsMagick中使用，identify 命令获取图片信息
	 * @param ext
	 *            指定的图片格式，看图片信息中是否包涵该格式
	 * */
	private ImageInfo parseImageInfo(String info, String ext) {
		int index = info.indexOf(ext);
		if (index == -1) {
			return null;
		}

		int indexOfSpace = info.indexOf(" ", index);
		int indexOfX = info.indexOf("x", indexOfSpace);
		int indexOfPlus = info.indexOf("+", indexOfX);
		String width = info.substring(indexOfSpace + 1, indexOfX);
		String height = info.substring(indexOfX + 1, indexOfPlus);

		if ("JPEG".equals(ext)) { // 对于JPG文件解析的文件格式为JPEG,需要做一次转换
			ext = "JPG";
		}

		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("图片格式[" + ext + "]，宽度[" + width + "]，高度[" + height + "]");
		}

		ImageInfo imageInfo = new ImageInfo();
		imageInfo.setExt(ext.toLowerCase());
		imageInfo.setHeight(Integer.parseInt(height));
		imageInfo.setWidth(Integer.parseInt(width));
		return imageInfo;
	}
}
